<template>
    <div class="dialog">
        <el-dialog
            v-model="dialogVisible"
            :title="isNewRef ? formDialogConfig.newTitle : formDialogConfig.editTitle"
            :width="formDialogConfig.width"
            :fullscreen="formDialogConfig.fullscreen"
            :top="formDialogConfig.top"
            :modal="formDialogConfig.modal"
            :modal-class="formDialogConfig.modalClass"
            :append-to-body="formDialogConfig.appendToBody"
            :lock-scroll="formDialogConfig.lockScroll"
            :custom-class="formDialogConfig.customClass"
            :open-delay="formDialogConfig.openDelay"
            :close-delay="formDialogConfig.closeDelay"
            :close-on-click-modal="formDialogConfig.closeOnClickModal"
            :close-on-press-escape="formDialogConfig.closeOnPressEscape"
            :show-close="formDialogConfig.showClose"
            :draggable="formDialogConfig.draggable"
            :center="formDialogConfig.center"
            :align-center="formDialogConfig.alignCenter"
            :destroy-on-close="formDialogConfig.destroyOnClose"
            :close-icon="formDialogConfig.closeIcon"
            :z-index="formDialogConfig.zIndex"
            :header-aria-level="formDialogConfig.headerAriaLevel"
            @close="closeDialog"
        >
            <slot></slot>
            <div class="form">
                <el-form
                    ref="formRef"
                    :model="formData"
                    :rules="formRuler"
                    :inline="formDialogConfig.inline"
                    :label-position="formDialogConfig.labelPosition"
                    :label-width="formDialogConfig.labelWidth"
                    :label-suffix="formDialogConfig.labelSuffix"
                    :hide-required-asterisk="formDialogConfig.hideRequiredAsterisk"
                    :require-asterisk-position="formDialogConfig.requireAsteriskPosition"
                    :show-message="formDialogConfig.showMessage"
                    :inline-message="formDialogConfig.inlineMessage"
                    :status-icon="formDialogConfig.statusIcon"
                    :validate-on-rule-change="formDialogConfig.validateOnRuleChange"
                    size="large"
                    :disabled="formDialogConfig.disabled"
                    :scroll-to-error="formDialogConfig.scrollToError"
                    :scroll-into-view-options="formDialogConfig.scrollIntoViewOptions"
                >
                    <el-row :gutter="10">
                        <template v-for="item of formDialogConfig.formItems" :key="item.prop">
                            <el-col :span="formDialogConfig.span">
                                <el-form-item
                                    :label="item.label"
                                    :prop="item.prop"
                                    :label-width="item.labelWidth"
                                    :required="item.required"
                                    :rules="item.rules"
                                    :error="item.error"
                                    :show-message="item.showMessage"
                                    :inline-message="item.inlineMessage"
                                    :size="item.size"
                                    :for="item.for"
                                    :validate-status="item.validateStatus"
                                >
                                    <!-- 输入框 -->
                                    <template v-if="item.type === 'input'">
                                        <el-input
                                            :type="item.inputType"
                                            v-model="formData[item.prop]"
                                            :maxlength="item.maxlength"
                                            :minlength="item.minlength"
                                            :show-word-limit="item.showWordLimit"
                                            :placeholder="item.placeholder"
                                            :clearable="item.clearable"
                                            :formatter="item.formatter"
                                            :parser="item.parser"
                                            :show-password="item.showPassword"
                                            :disabled="item.disabled"
                                            :size="item.inputSize"
                                            :prefix-icon="item.prefixIcon"
                                            :suffix-icon="item.suffixIcon"
                                            :rows="item.rows"
                                            :autosize="item.autosize"
                                            :autocomplete="item.autocomplete"
                                            :name="item.name"
                                            :readonly="item.readonly"
                                            :max="item.max"
                                            :min="item.min"
                                            :step="item.step"
                                            :resize="item.resize"
                                            :autofocus="item.autofocus"
                                            :form="item.form"
                                            :tabindex="item.tabindex"
                                            :validate-event="item.validateEvent"
                                            :input-style="item.inputStyle"
                                        />
                                    </template>

                                    <!-- 时间选择器 -->
                                    <template v-if="item.type === 'time-picker'">
                                        <div class="timepicker" style="width: 100%">
                                            <el-time-select
                                                v-model="formData[item.prop]"
                                                :disabled="item.disabled"
                                                :editable="item.editable"
                                                :clearable="item.clearable"
                                                :size="item.size"
                                                :placeholder="item.placeholder"
                                                :format="item.format"
                                                :default-value="item.defaultValue"
                                                :prefix-icon="item.prefixIcon"
                                                :clear-icon="item.clearIcon"
                                                :start="item.start"
                                                :end="item.end"
                                                :step="item.step"
                                                style="width: 100%"
                                            />
                                        </div>
                                    </template>

                                    <!-- 日期选择器 -->
                                    <template v-if="item.type === 'date-picker'">
                                        <div class="datepicker" style="width: 100%">
                                            <el-date-picker
                                                v-model="formData[item.prop]"
                                                :readonly="item.readonly"
                                                :disabled="item.disabled"
                                                :size="item.size"
                                                :editable="item.editable"
                                                :placeholder="item.placeholder"
                                                :start-placeholder="item.startPlaceholder"
                                                :end-placeholder="item.endPlaceholder"
                                                :type="item.DatePickerType"
                                                :format="item.format"
                                                :popper-class="item.popperClass"
                                                :popper-options="item.popperOptions"
                                                :range-separator="item.rangeSeparator"
                                                :default-value="item.defaultValue"
                                                :default-time="item.defaultTime"
                                                :value-format="item.valueFormat"
                                                :id="item.id"
                                                :name="item.name"
                                                :unlink-panels="item.unlinkPanels"
                                                :prefix-icon="item.prefixIcon"
                                                :clear-icon="item.clearIcon"
                                                :validate-event="item.validateEvent"
                                                :disabled-date="item.disabledDate"
                                                :shortcuts="item.shortcuts"
                                                :cell-class-name="item.cellClassName"
                                                :teleported="item.teleported"
                                                style="width: 100%"
                                            ></el-date-picker>
                                        </div>
                                    </template>

                                    <!-- 下拉框 -->
                                    <template v-if="item.type === 'select'">
                                        <el-select
                                            v-model="formData[item.prop]"
                                            :multiple="item.multiple"
                                            :disabled="item.disabled"
                                            :value-key="item.valueKey"
                                            :size="item.selectSize"
                                            :clearable="item.clearable"
                                            :collapse-tags="item.collapseTags"
                                            :collapse-tags-tooltip="item.collapseTagsTooltip"
                                            :multiple-limit="item.multipleLimit"
                                            :name="item.name"
                                            :effect="item.effect"
                                            :autocomplete="item.autocomplete"
                                            :placeholder="item.placeholder"
                                            :filterable="item.filterable"
                                            :allow-create="item.allowCreate"
                                            :filter-method="item.filterMethod"
                                            :remote="item.remote"
                                            :remote-method="item.remoteMethod"
                                            :remote-show-suffix="item.remoteShowSuffix"
                                            :loading="item.loading"
                                            :loading-text="item.loadingText"
                                            :no-match-text="item.noMatchText"
                                            :no-data-text="item.noDataText"
                                            :popper-class="item.popperClass"
                                            :reserve-keyword="item.reserveKeyword"
                                            :default-first-option="item.defaultFirstOption"
                                            :teleported="item.teleported"
                                            :persistent="item.persistent"
                                            :automatic-dropdown="item.automaticDropdown"
                                            :clear-icon="item.clearIcon"
                                            :fit-input-width="item.fitInputWidth"
                                            :suffix-icon="item.suffixIcon"
                                            :suffix-transition="item.suffixTransition"
                                            :tag-type="item.tagType"
                                            :validate-event="item.validateEvent"
                                            :placement="item.placement"
                                            :max-collapse-tags="item.maxCollapseTags"
                                            :popper-options="item.popperoptions"
                                            :aria-label="item.ariaLabel"
                                            style="width: 100%"
                                        >
                                            <template v-if="item.hasOwnProperty('options')">
                                                <template
                                                    v-for="option of item.options"
                                                    :key="option.value"
                                                >
                                                    <el-option
                                                        :label="option.label"
                                                        :value="option.value"
                                                    />
                                                </template>
                                            </template>
                                            <template v-else>
                                                <template
                                                    v-for="option of dcitData[item.prop]"
                                                    :key="option.code"
                                                >
                                                    <el-option
                                                        :label="option.item1"
                                                        :value="option.code"
                                                    />
                                                </template>
                                            </template>
                                        </el-select>
                                    </template>

                                    <!-- 单选框 -->
                                    <template v-if="item.type === 'radio'">
                                        <el-radio-group
                                            v-model="formData[item.prop]"
                                            :size="item.size"
                                            :disabled="item.disabled"
                                            :text-color="item.textColor"
                                            :fill="item.fill"
                                            :validate-event="item.validateEvent"
                                        >
                                            <template
                                                v-for="option of item.options"
                                                :key="option.label"
                                            >
                                                <el-radio :label="option.value">{{
                                                    option.label
                                                }}</el-radio>
                                            </template>
                                        </el-radio-group>
                                    </template>

                                    <!-- 自定义 -->
                                    <template v-if="item.type === 'custom'">
                                        <slot :name="item.slotName"></slot>
                                    </template>
                                </el-form-item>
                            </el-col>
                        </template>
                    </el-row>
                </el-form>
            </div>
            <template #footer>
                <span class="dialog-footer">
                    <el-button @click="closeDialog">取消</el-button>
                    <el-button type="primary" @click="handleConfirmClick">确定</el-button>
                </span>
            </template>
        </el-dialog>
    </div>
</template>

<script setup lang="ts">
import { PropType, getCurrentInstance, reactive, ref } from "vue"

import { newPageData, editPageData, getSysDict } from "@/api/common/common"
import { MessageError, MessageSuccess } from "@/utils/message"
import { ElForm } from "element-plus"
import { EpPropMergeType } from "element-plus/es/utils/vue/props/types"

type formItem = any[]
type ILabelPosition = EpPropMergeType<StringConstructor, "top" | "right" | "left", unknown>
type IRequireAsteriskPosition = EpPropMergeType<StringConstructor, "right" | "left", unknown>
type ISize = EpPropMergeType<StringConstructor, "" | "default" | "small" | "large", unknown>

//定义props
const props = defineProps({
    formDialogConfig: {
        type: Object,
        default() {
            return {
                /*  dialog Attributes  */
                newTitle: {
                    //Dialog 对话框 Dialog 的标题
                    type: String,
                    default: "新增",
                },
                editTitle: {
                    //Dialog 对话框 Dialog 的标题
                    type: String,
                    default: "编辑",
                },
                newUrl: {
                    type: String,
                },
                editUrl: {
                    type: String,
                },
                newMethod: {
                    type: String,
                },
                editMethod: {
                    type: String,
                },
                width: {
                    //对话框的宽度，默认值为 50%
                    type: String,
                    default: "",
                },
                fullscreen: {
                    //是否为全屏 Dialog
                    type: Boolean,
                    default: false,
                },
                top: {
                    //dialog CSS 中的 margin-top 值，默认为 15vh
                    type: String,
                    default: "",
                },
                modal: {
                    //是否需要遮罩层
                    type: Boolean,
                    default: true,
                },
                modalClass: {
                    //遮罩的自定义类名
                    type: String,
                },
                appendToBody: {
                    //Dialog 自身是否插入至 body 元素上。 嵌套的 Dialog 必须指定该属性并赋值为 true
                    type: Boolean,
                    default: false,
                },
                lockScroll: {
                    //是否在 Dialog 出现时将 body 滚动锁定
                    type: Boolean,
                    default: true,
                },
                customClass: {
                    //Dialog 的自定义类名
                    type: String,
                },
                openDelay: {
                    //dialog 打开的延时时间，单位毫秒
                    type: Number,
                    default: 0,
                },
                closeDelay: {
                    //drawer 关闭的延时时间，单位毫秒
                    type: Number,
                    default: 0,
                },
                closeOnClickModal: {
                    //是否可以通过点击 modal 关闭 Dialog
                    type: Boolean,
                    default: true,
                },
                closeOnPressEscape: {
                    //是否可以通过按下 ESC 关闭 Dialog
                    type: Boolean,
                    default: true,
                },
                showClose: {
                    //是否显示关闭按钮
                    type: Boolean,
                    default: true,
                },
                beforeClose: {
                    //关闭前的回调，会暂停 Dialog 的关闭. 回调函数内执行 done 参数方法的时候才是真正关闭对话框的时候.
                    type: Function,
                },
                draggable: {
                    //为 Dialog 启用可拖拽功能
                    type: Boolean,
                    default: false,
                },
                center: {
                    //是否让 Dialog 的 header 和 footer 部分居中排列
                    type: Boolean,
                    default: false,
                },
                alignCenter: {
                    //是否水平垂直对齐对话框
                    type: Boolean,
                    default: false,
                },
                destroyOnClose: {
                    //当关闭 Dialog 时，销毁其中的元素
                    type: Boolean,
                    default: false,
                },
                closeIcon: {
                    //自定义关闭图标，默认 Close
                    type: String,
                },
                zIndex: {
                    //和原生的 CSS 的 z-index 相同，改变 z 轴的顺序
                    type: Number,
                },
                headerAriaLevel: {
                    //header 的 aria-level 属性
                    type: String,
                    default: "2",
                },
                span: {
                    type: Number,
                    default: 24,
                },
                formItems: {
                    type: Array as PropType<formItem>,
                    default: () => [],
                },
                /*  dialog Attributes  */
                model: {
                    //表单数据对象
                    type: Object,
                },
                rules: {
                    //	表单验证规则
                    type: Object,
                },
                inline: {
                    //行内表单模式
                    type: Boolean,
                    default: false,
                },
                labelPosition: {
                    //表单域标签的位置， 当设置为 left 或 right 时，则也需要设置 label-width 属性
                    type: String as PropType<ILabelPosition>,
                    default: "right",
                },
                labelWidth: {
                    //标签的长度，例如 '50px'。 作为 Form 直接子元素的 form-item 会继承该值。 可以使用 auto。
                    type: String,
                    default: "",
                },
                labelSuffix: {
                    //表单域标签的后缀
                    type: String,
                    default: "",
                },
                hideRequiredAsterisk: {
                    //是否隐藏必填字段标签旁边的红色星号。
                    type: Boolean,
                    default: false,
                },
                requireAsteriskPosition: {
                    //星号的位置。
                    type: String as PropType<IRequireAsteriskPosition>,
                    default: "left",
                },
                showMessage: {
                    //是否显示校验错误信息
                    type: Boolean,
                    default: true,
                },
                inlineMessage: {
                    //是否以行内形式展示校验信息
                    type: Boolean,
                    default: false,
                },
                statusIcon: {
                    //是否在输入框中显示校验结果反馈图标
                    type: Boolean,
                    default: false,
                },
                validateOnRuleChange: {
                    //是否在 rules 属性改变后立即触发一次验证
                    type: Boolean,
                    default: true,
                },
                size: {
                    //用于控制该表单内组件的尺寸
                    type: String as PropType<ISize>,
                },
                disabled: {
                    //是否禁用该表单内的所有组件。 如果设置为 true, 它将覆盖内部组件的 disabled 属性
                    type: Boolean,
                    default: false,
                },
                scrollToError: {
                    //当校验失败时，滚动到第一个错误表单项
                    type: Boolean,
                    default: true,
                },
                scrollIntoViewOptions: {
                    //当校验有失败结果时，滚动到第一个失败的表单项目 可通过 scrollIntoView 配置
                    type: Boolean,
                },
            }
        },
    },
    otherInfo: {
        type: Object,
    },
})

//定义自定义事件
const emit = defineEmits(["refresh", "beforeConfirm"])

//使用getCurrentInstance方法获取添加到Vue全局的表单校验方法
const proxy = getCurrentInstance()?.appContext.config.globalProperties
const ruler = proxy?.$formRulers

//定义内部的属性
const initialData: any = {}
const formRuler = reactive<any>({})
for (const item of props.formDialogConfig.formItems) {
    initialData[item.prop] = item.initialValue ?? ""
}
const formData = reactive<any>(initialData)
const isNewRef = ref(true)
const editData = ref()
const dialogVisible = ref<boolean>(false)
const formRef = ref<InstanceType<typeof ElForm>>()
const dcitData = ref<any>({})

/**
 * 获取所有字典数据
 *
 * @returns 无返回值
 */
function getAllDictData() {
    props.formDialogConfig.formItems.forEach((item: any) => {
        if (item.type === "select" && !item.hasOwnProperty("options")) {
            getSysDict(item.params).then((res) => {
                if (res.code == 0) {
                    dcitData.value[item.prop] = res.data
                }
            })
        }
    })
}

getAllDictData()

/**
 * 设置弹窗的显示状态
 *
 * @param isNew 是否为新建，默认为 true
 * @param itemData 编辑时的数据，可选
 */
function setModalVisible(isNew: boolean = true, itemData?: any) {
    dialogVisible.value = true
    isNewRef.value = isNew
    // for (const item of props.formDialogConfig.formItems) {
    //   formRuler[item.prop] = [ruler(item.label)]
    // }
    if (!isNew && itemData) {
        // 编辑数据

        //多选下拉框转换数组回显
        for (const key in itemData) {
            formData[key] = itemData[key]
            props.formDialogConfig.formItems.forEach((item: any) => {
                if (
                    item.type === "select" &&
                    item.hasOwnProperty("multiple") &&
                    item.prop === key &&
                    itemData[key]
                ) {
                    formData[key] = itemData[key].split(",")
                }
            })
        }
        editData.value = itemData
    } else {
        // 新建数据
        formData.value = initialData
        for (const key in formData) {
            const item = props.formDialogConfig.formItems.find((item: any) => item.prop === key)
            formData[key] = item ? item.initialValue : ""
        }
        editData.value = null
    }
}

/**
 * 点击确认按钮的处理函数
 */
function handleConfirmClick() {
    //多选下拉框结果转换字符串提交
    props.formDialogConfig.formItems.forEach((item: any) => {
        if (item.type === "select" && item.hasOwnProperty("multiple")) {
            for (const key in formData) {
                if (key === item.prop) {
                    formData[key] = formData[key].toString()
                }
            }
        }
    })
    let infoData = formData
    if (props.otherInfo) {
        infoData = { ...infoData, ...props.otherInfo }
    }

    if (!isNewRef.value && editData.value) {
        // 编辑数据
        editPageData(
            props.formDialogConfig.editUrl,
            props.formDialogConfig.editMethod,
            infoData
        ).then((res) => {
            if (res.code == 0) {
                MessageSuccess("upd")
                closeDialog()
            } else {
                MessageError("upd")
                closeDialog()
            }
        })
    } else {
        // 新建数据
        newPageData(props.formDialogConfig.newUrl, props.formDialogConfig.newMethod, infoData).then(
            (res) => {
                if (res.code == 0) {
                    MessageSuccess("add")
                    closeDialog()
                } else {
                    MessageError("add")
                    closeDialog()
                }
            }
        )
    }
}

/**
 * 关闭对话框
 *
 * @remarks
 * 将对话框的显示状态设置为隐藏，重置表单字段，并触发刷新事件
 */
function closeDialog() {
    dialogVisible.value = false
    formRef.value?.resetFields()
    emit("refresh")
}

// 暴露的属性和方法
defineExpose({ setModalVisible })
</script>

<style lang="scss" scoped>
.form {
    padding: 0 20px;

    /* 日期选择器宽度100% */
    .datapicker {
        width: 100%;
    }

    :deep(.el-input__wrapper) {
        width: 96%;
    }
}

:deep(.el-form .el-form-item:last-of-type) {
    margin-bottom: 10px !important;
}
</style>
@/api/common/common
